﻿/**
* @description  Class to load and manage sounds for UI events. 
*/
package com.foed.managers {

	import flash.events.IOErrorEvent;
	import flash.media.Sound;
	import flash.media.SoundTransform;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.Dictionary;

	import com.foed.IDescribable;

	public class SoundManager {

		private static var sSoundManager:SoundManager;
		private var _sounds:Object;
		private var _listeners:Dictionary;
	
		/**
		* @description  Constructor for Singleton class.
		*/
		public function SoundManager() {
			init();
		}
		
		/**
		* @description	Returns the Singleton instance of the SoundManager.
		*
		* @returns  The only SoundManager instance.
		*/
		public static function getInstance():SoundManager {
			if (sSoundManager == null) sSoundManager = new SoundManager();
			return sSoundManager;
		}
	
		/**
		* @description  Initializes properties. 
		*/
		private function init():void {
			_sounds = {};
			_listeners = new Dictionary();
		}
		
		/**
		* @description	Plays the specified sound at the specified volume.
		*
		* @param  pSoundObj  The object containing properties defining how a sound should be played back.
		*/
		private function play(pSoundObj:SoundObject):void {
			Sound(pSoundObj.sound).play(0, 0, new SoundTransform(Number(pSoundObj.volume)));
		}
		
		/**
		* @description	Event handler for error on loading sound.
		*
		* @param  pEvent  Event fired by Sound.
		*/
		private function onIOError(pEvent:IOErrorEvent):void {
			trace("error loading sound");
		}
		
		/**
		* @description  Adds a sound to be loaded to the collection.
		*
		* @param  pSoundName  The name by which the sound file will be stored.
		* @param  pSoundPath  The path to the MP3 file for the sound.
		*/
		public function addSound(pSoundName:String, pSoundPath:String):void {
			_sounds[pSoundName] = pSoundPath;
		}
		
		/**
		* @description  Plays a sound for the specified event on the specified component, if it is registered.
		*
		* @param  pObj  The IDescriable instance for which to play the sound.
		* @param  pEvent  The event the sound would be registered for.
		*/
		public function playSound(pObj:IDescribable, pEvent:String):void {
			var pListener:Object = _listeners[pObj];
			var pSoundObj:SoundObject;
			if (pListener != null) {
				pSoundObj = pListener[pEvent] as SoundObject;
				if (pSoundObj != null) {
					play(pSoundObj);
					return;
				}
			}
			var pClasses:Array = pObj.getClassHierarchy();
			for (var i:int = pClasses.length-1; i > -1; i--) {
				pListener = _listeners[pClasses[i]];
				if (pListener != null) {
					pSoundObj = pListener[pEvent] as SoundObject;
					if (pSoundObj != null) {
						play(pSoundObj);
						return;
					}
				}
			}
		}
	
		/**
		* @description  Registers an ID string (usually a fully qualified class name) for a sound to occur on the specified event. 
		*
		* @param  pID  The ID to which the sound will be applied (usually a fully qualified class name).
		* @param  pSoundName  The name of the sound in the collection to utilize.
		* @param  pEvent  The event to which to apply the sound.
		* @param  pVolume  The volume at which to play the sound for the specified event.
		*/
		public function registerForSound(
			pID:Object,
			pSoundName:String,
			pEvent:String,
			pVolume:Number=1
		):void {
			var pSound:Sound = new Sound();
			var pPath:String = _sounds[pSoundName] + ".mp3";
			try {
				pSound.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
				pSound.load(new URLRequest(pPath));
				if (_listeners[pID] == null) _listeners[pID] = {};
				_listeners[pID][pEvent] = new SoundObject(pSound, pVolume);
			} catch (e:Error) {}
		}
	
	}
	
}

import flash.media.Sound;

/**
* @description  Internal class that defines a sound to be played. 
*/
class SoundObject {

	private var mSound:Sound;
	private var mVolume:Number;
	
	/**
	* @description  Constructor. 
	*
	* @param  pSound  The Sound object to play upon request.
	* @param  pVolume  The volume at which to play the sound.
	*/
	public function SoundObject(pSound:Sound, pVolume:Number) {
		mSound = pSound;
		mVolume = pVolume;
	}
	
	/**
	* @description  Returns the Sound instance to play.
	*
	* @returns  The Sound instance to play.
	*/
	public function get sound():Sound { return mSound; }

	/**
	* @description  Returns the volume at which to play the Sound instance.
	*
	* @returns  The volume at which to play the Sound instance.
	*/
	public function get volume():Number { return mVolume; }

}